using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;



using System;



public class Parser {
	public const int _EOF = 0;
	public const int _Literal = 1;
	public const int maxT = 22;

	const bool _T = true;
	const bool _x = false;
	const int minErrDist = 0;
	
	public Scanner scanner;
	public Errors  errors;

	public Token t;    // last recognized token
	public Token la;   // lookahead token
	int errDist = minErrDist;

public string Version {get {return "0.1";}}


	List<string> inst = new List<string>();
	
	private void PushInstruction(string instruction, int pos = 0) 
    {
		if (pos == 0)
		{
			inst.Add(instruction);
		} 
		else 
		{
			
			if (inst.Count - pos > 0) 
			{
				inst.Insert(inst.Count - pos, instruction);
			}

		}
	}

	class MultiplicityContext 
	{
		public bool IsGreedy {get;set;}
		public string Operator {get;set;}
		public bool IsActive {get;set;}

        public MultiplicityContext()
        {
            Reset();
        }

        public void Reset()
        {
            IsGreedy = true;
            Operator = "";
            IsActive = false;
        }

		public override string ToString() {
            return Operator + (IsGreedy ? "" : "?");
		}
	}

	MultiplicityContext mContext = new MultiplicityContext();

	void SetMOperator(string op) 
	{
		mContext.Operator = op;
		mContext.IsActive = true;
	}

	void SetMGreedy(bool isGreedy)
	{
		mContext.IsGreedy = isGreedy;
		mContext.IsActive = true;
	}

	string PopMultiplcityContext() 
	{
		string context = mContext.ToString();
		mContext.Reset();
		return context;
	}
	
	public List<string> Instructions {
		get 
		{
			return inst;
		}
	}

	public void ResetInstructions() 
	{
		inst.Clear();
	}



	public Parser(Scanner scanner) {
		this.scanner = scanner;
		errors = new Errors();
	}

	void SynErr (int n) {
		if (errDist >= minErrDist) errors.SynErr(la.line, la.col, n);
		errDist = 0;
	}

	public void SemErr (string msg) {
		if (errDist >= minErrDist) errors.SemErr(t.line, t.col, msg);
		errDist = 0;
	}
	
	void Get () {
		for (;;) {
			t = la;
			la = scanner.Scan();
			if (la.kind <= maxT) { ++errDist; break; }

			la = t;
		}
	}
	
	void Expect (int n) {
		if (la.kind==n) Get(); else { SynErr(n); }
	}
	
	bool StartOf (int s) {
		return set[s, la.kind];
	}
	
	void ExpectWeak (int n, int follow) {
		if (la.kind == n) Get();
		else {
			SynErr(n);
			while (!StartOf(follow)) Get();
		}
	}


	bool WeakSeparator(int n, int syFol, int repFol) {
		int kind = la.kind;
		if (kind == n) {Get(); return true;}
		else if (StartOf(repFol)) {return false;}
		else {
			SynErr(n);
			while (!(set[syFol, kind] || set[repFol, kind] || set[0, kind])) {
				Get();
				kind = la.kind;
			}
			return StartOf(syFol);
		}
	}

	
	void Segex() {
		ResetInstructions();
		if (la.kind == 2) {
			LineStartOp();
		}
		WordMatchOp();
		while (StartOf(1)) {
			WordMatchOp();
		}
		if (la.kind == 3) {
			LineEndOp();
		}
	}

	void LineStartOp() {
		Expect(2);
		PushInstruction(@"^");
	}

	void WordMatchOp() {
		if (la.kind == 1) {
			LiteralOp();
		} else if (StartOf(2)) {
			CharacterMatchOp();
		} else if (la.kind == 20) {
			CaptureOp();
		} else SynErr(23);
	}

	void LineEndOp() {
		Expect(3);
		PushInstruction(@"$");
	}

	void LiteralOp() {
		Expect(1);
		PushInstruction(t.val.Replace("\"",""));
	}

	void CharacterMatchOp() {
		MultiplictyOp();
		PushInstruction(@"[");
		CharacterOp();
		if (la.kind == 19) {
			OrOp();
		}
		PushInstruction(@"]"); PushInstruction(PopMultiplcityContext());
	}

	void CaptureOp() {
		GroupStartOp();
		while (StartOf(1)) {
			WordMatchOp();
		}
		GroupStopOp();
	}

	void MultiplictyOp() {
		switch (la.kind) {
		case 11: case 12: case 13: {
			if (la.kind == 13) {
				LazyOp();
			}
			if (la.kind == 11) {
				OneOrMoreOp();
			} else if (la.kind == 12) {
				ZeroOrMoreOp();
			} else SynErr(24);
			break;
		}
		case 14: {
			OneOp();
			break;
		}
		case 15: {
			TwoOp();
			break;
		}
		case 16: {
			ThreeOp();
			break;
		}
		case 17: {
			FourOp();
			break;
		}
		case 18: {
			FiveOp();
			break;
		}
		default: SynErr(25); break;
		}
	}

	void CharacterOp() {
		switch (la.kind) {
		case 4: {
			WhiteSpaceOp();
			break;
		}
		case 5: {
			NumberOp();
			break;
		}
		case 6: {
			LetterOp();
			break;
		}
		case 10: {
			AnyOp();
			break;
		}
		case 7: {
			PunctuationOp();
			break;
		}
		case 8: {
			SymbolOp();
			break;
		}
		case 9: {
			DecimalOp();
			break;
		}
		default: SynErr(26); break;
		}
	}

	void OrOp() {
		Expect(19);
		CharacterOp();
		while (la.kind == 19) {
			Get();
			CharacterOp();
		}
	}

	void WhiteSpaceOp() {
		PushInstruction(@"\p{Z}");
		Expect(4);
	}

	void NumberOp() {
		PushInstruction(@"\p{N}");
		Expect(5);
	}

	void LetterOp() {
		PushInstruction(@"\p{L}");
		Expect(6);
	}

	void AnyOp() {
		PushInstruction(@"\w\W");
		Expect(10);
	}

	void PunctuationOp() {
		PushInstruction(@"\p{P}");
		Expect(7);
	}

	void SymbolOp() {
		PushInstruction(@"\p{S}");
		Expect(8);
	}

	void DecimalOp() {
		PushInstruction(@",\.\p{N}");
		Expect(9);
	}

	void LazyOp() {
		SetMGreedy(false);
		Expect(13);
	}

	void OneOrMoreOp() {
		SetMOperator(@"+");
		Expect(11);
	}

	void ZeroOrMoreOp() {
		SetMOperator(@"*");
		Expect(12);
	}

	void OneOp() {
		SetMOperator(@"{1}");
		Expect(14);
	}

	void TwoOp() {
		SetMOperator(@"{2}");
		Expect(15);
	}

	void ThreeOp() {
		SetMOperator(@"{3}");
		Expect(16);
	}

	void FourOp() {
		SetMOperator(@"{4}");
		Expect(17);
	}

	void FiveOp() {
		SetMOperator(@"{5}");
		Expect(18);
	}

	void GroupStartOp() {
		Expect(20);
		PushInstruction(@"(?<CAPTURE>");
	}

	void GroupStopOp() {
		Expect(21);
		PushInstruction(@")");
	}



	public void Parse() {

		errors.Clear();

		la = new Token();
		la.val = "";		
		Get();
		Segex();
		Expect(0);

	}
	
	static readonly bool[,] set = {
		{_T,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_x},
		{_x,_T,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_T,_T,_T, _T,_T,_T,_x, _T,_x,_x,_x},
		{_x,_x,_x,_x, _x,_x,_x,_x, _x,_x,_x,_T, _T,_T,_T,_T, _T,_T,_T,_x, _x,_x,_x,_x}

	};
} // end Parser


public class Errors {
	public int count = 0;                                    // number of errors detected
	public StringBuilder errors = new StringBuilder();
	public string errMsgFormat = "-- line {0} col {1}: {2}"; // 0=line, 1=column, 2=text

	public virtual void SynErr (int line, int col, int n) {
		string s;
		switch (n) {
			case 0: s = "EOF expected"; break;
			case 1: s = "Literal expected"; break;
			case 2: s = "\"startofline\" expected"; break;
			case 3: s = "\"endofline\" expected"; break;
			case 4: s = "\"whitespace\" expected"; break;
			case 5: s = "\"number\" expected"; break;
			case 6: s = "\"letter\" expected"; break;
			case 7: s = "\"punctuation\" expected"; break;
			case 8: s = "\"symbol\" expected"; break;
			case 9: s = "\"decimal\" expected"; break;
			case 10: s = "\"any\" expected"; break;
			case 11: s = "\"oneormore\" expected"; break;
			case 12: s = "\"zeroormore\" expected"; break;
			case 13: s = "\"lazy\" expected"; break;
			case 14: s = "\"one\" expected"; break;
			case 15: s = "\"two\" expected"; break;
			case 16: s = "\"three\" expected"; break;
			case 17: s = "\"four\" expected"; break;
			case 18: s = "\"five\" expected"; break;
			case 19: s = "\"or\" expected"; break;
			case 20: s = "\"(\" expected"; break;
			case 21: s = "\")\" expected"; break;
			case 22: s = "??? expected"; break;
			case 23: s = "invalid WordMatchOp"; break;
			case 24: s = "invalid MultiplictyOp"; break;
			case 25: s = "invalid MultiplictyOp"; break;
			case 26: s = "invalid CharacterOp"; break;

			default: s = "error " + n; break;
		}
		errors.AppendLine(string.Format(errMsgFormat, line, col, s));
		count++;
	}

	public virtual void SemErr (int line, int col, string s) {
		errors.AppendLine(string.Format(errMsgFormat, line, col, s));
		count++;
	}
	
	public virtual void SemErr (string s) {
		errors.AppendLine(string.Format(s));
		count++;
	}
	
	public virtual void Warning (int line, int col, string s) {
		errors.AppendLine(string.Format(errMsgFormat, line, col, s));
	}
	
	public virtual void Warning(string s) {
		errors.AppendLine(string.Format(s));
	}

	public virtual void Clear() 
	{
		errors.Clear();
	}
} // Errors


public class FatalError: Exception {
	public FatalError(string m): base(m) {}
}
